<?php
namespace system\core;

use system\libraries\Template;
use system\libraries\Session;
use system\libraries\Acl;
use system\libraries\Translation;

/**
 * Router
 * 
 * The Router class handles the call to the controller.
 * 
 * @author Steffen Brem <steffenbrem@gmail.com>
 * @package System.core
 */
class Router 
{
    /**
     * Stores an instance of the Request class
     *  
     * @var object
     */
    private $_request;
    
    /**
     * Handles the Request
     * 
     * @access public
     */
    public function __construct()
    {
        $this->_request = new Request();
    }
    
    /**
     * Get the request object
     * 
     * @access public
     * @return object
     */
    public function getRequest()
    {
        return $this->_request;
    }
    
    /**
     * This method is called before the dispatch takes place
     * 
     * @access private
     */
    public function beforeDispatch()
    {
        
    }
    
    /**
     * Pre dispatch
     * 
     * This method is called after we checked if the request is a valid controller
     * action. But before the actual dispatch!
     * 
     * @access private
     * @return void
     */
    private function _preDispatch()
    {
        // -- HELPERS ----------------------------------------------------------
        
        $helpers = Config::get('autoload', 'helpers', FALSE);
        
        if ( is_array($helpers) )
        {
            foreach ( $helpers as $helper )
            {
                if ( ! file_exists(SYS_PATH . 'helpers/' . $helper . '_helper.php') )
                {
                    throw new \Exception('Cannot load helper "' . $helper . '", helper does not exist.');
                }
                else
                {
                    include_once SYS_PATH . 'helpers/' . $helper . '_helper.php';
                }
            }
        }
        
        // -- MODULES ----------------------------------------------------------
        
        if ( $this->_request->getModule() )
        {
            // This request is a module
            $bootstrapClass = '\application\modules\\' . $this->_request->getModule() . '\Hosselaar';
            
            $bootstrap = new $bootstrapClass();
            $bootstrap->__init();
        }
        else
        {
            $bootstrap = new \application\Hosselaar();
            $bootstrap->__init();
        }
        
        
        // -- ACL --------------------------------------------------------------
        
        $acl = Acl::getInstance();
        if ( $acl ) 
        {
            // If the __init() method of the application bootstrap did not check
            // the ACL, we will do it now (if there is a ACL ofcourse)...
            $acl->checkAcl();
        }
        
        // -- TRANSLATION ------------------------------------------------------
        
        if ( Config::get('translation', 'auto_load') == TRUE )
        {
            $translation = new Translation();
            $translation->load(strtolower($this->_request->getController()));
        }
    }
    
    /**
     * Dispatch application
     * 
     * @access public
     */
    public function dispatch()
    {
        // Parse any custom routing made in settings.ini
        $this->_parseRouting();
        
        $action    = $this->_request->getAction();
        $namespace = ( $this->_request->getModule() ) 
                ? 'application\modules\\' . $this->_request->getModule() . '\controllers\\' 
                : 'application\controllers\\';
        
        $controllerClass = $namespace . ucfirst($this->_request->getController());
         
        if ( Autoloader::classFileExists($controllerClass) )
        {
            // If the action affects a callback, set the action to NULL
            if ( preg_match("/callback_/", $action) )
            {
                $action = NULL;
            }
            
            // Hmm, the action method is callable...
            if ( ! is_callable( array($controllerClass, $action) ) )
            {
                throw new \Exception('This page does not exist.', 404);
            }
            
            // Pre-dispatcher! Fires after checking the files etc...
            $this->_preDispatch();
            
            // Create an instance of the controller
            $obj = new $controllerClass();
            
            // Save the Controller to the Registry. We do this because we can
            // make callbacks this way.
            Registry::set('controller_instance', $obj, TRUE);

            call_user_func_array( array($obj, $action), $this->_request->getParameters() );
         
            if ( Config::get('template', 'auto_render') == TRUE )
            {
                $template = Template::getInstance();

                // Template must not be FALSE, if it is, it is disabled
                if ( $template )
                {                    
                    $template->writeView('content', $this->_request->getController() . '/' . $action);
                    
                    $template->render();
                }
            }
        }
        else
        {
            throw new \Exception('The page "' . $this->_request->getController() . '" does not exist.', 404);
        }
    }
    
    /**
     * Parse the routing in settings.ini
     */
    private function _parseRouting()
    {
        $routing = Config::get('routing');
        
        if ( ! $routing ) { return; }
        
        $request = strtolower(implode('/', $this->_request->getSegments()));
        
        if ( isset($routing[$request]) )
        {
            return $this->_request->setSegments(explode('/', $routing[$request]) );
        }
        
        foreach ( $routing as $key => $value )
        {
            // Convert wildcards
            $key = str_replace(':any:', '(.*)', $key);
            
            if ( preg_match('#^' . $key . '$#', $request) )
            {
                $value = rtrim($value);
                
                // Only replace back-references when there is one
                if ( strpos($value, '$') )
                {
                    $value = preg_replace('#^'.$key.'$#', $value, $request);
                }
                
                return $this->_request->setSegments(explode('/', strtolower($value)) );
            }
        }
        
        return $this->_request->setSegments(explode('/', strtolower($value)) );
    }
}

/* End of file Router.php */